/*
** Copyright (C) 2010 Calin Radoni
**
** This file is part of CHScanner.
**
** CHScanner is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** CHScanner is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with CHScanner. If not, see <http://www.gnu.org/licenses/>.
*/

// DlgMap.cpp : implementation file

#include "stdafx.h"
#include "CHScanner.h"
#include "DlgMap.h"
#include ".\dlgmap.h"

#include <shellapi.h>

#include "VersionNumber.h"

//---------------------------------------------------------------------------

#define MI_CONNECT_HTTP		1
#define MI_CONNECT_HTTPS	2
#define MI_CONNECT_FTP		3
#define MI_CONNECT_TELNET	4
#define MI_CONNECT_SSH		5
#define MI_CONNECT_CUSTOM	6
#define MI_CONNECT_EXPORT	7
#define MI_CONNECT_HELP		8

//---------------------------------------------------------------------------

IMPLEMENT_DYNAMIC(DlgMap, CRgnDialog)
DlgMap::DlgMap(CWnd* pParent /*=NULL*/) : CRgnDialog(DlgMap::IDD, pParent)
{
	dlgDet       = NULL;
	vendorList   = NULL;
	mcast4List   = NULL;
	portDescList = NULL;
}

DlgMap::~DlgMap()
{
}

//---------------------------------------------------------------------------

void DlgMap::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_MAP_LIST, listMap);
}

//---------------------------------------------------------------------------

BEGIN_MESSAGE_MAP(DlgMap, CRgnDialog)
	ON_NOTIFY(LVN_ITEMCHANGED, IDC_MAP_LIST, OnLvnItemchangedMapList)
	ON_NOTIFY(NM_RCLICK, IDC_MAP_LIST, OnNMRclickMapList)
	ON_NOTIFY(LVN_COLUMNCLICK, IDC_MAP_LIST, OnLvnColumnclickMapList)
END_MESSAGE_MAP()

//---------------------------------------------------------------------------

BOOL DlgMap::OnInitDialog()
{
	CRgnDialog::OnInitDialog();

	LVCOLUMN col;

	listMap.SendMessage(LVM_SETVIEW, LV_VIEW_DETAILS, 0);

	listMap.SetExtendedStyle(LVS_EX_FULLROWSELECT);

	col.mask = LVCF_TEXT | LVCF_FMT | LVCF_WIDTH;
	col.fmt = LVCFMT_CENTER;
	col.cx = 100;
	col.pszText = _T("IP Address");
	listMap.InsertColumn(0, &col);

	col.fmt = LVCFMT_LEFT;
	col.cx = 200;
	col.pszText = _T("MAC");
	listMap.InsertColumn(1, &col);

	SetWindowText(_T("Map"));

	bool res;
	res = true;
	res &= popupMenu.AddMenuItem(MI_CONNECT_HTTP,   _T("Connect HTTP"));
	res &= popupMenu.AddMenuItem(MI_CONNECT_HTTPS,  _T("Connect HTTPS"));
	res &= popupMenu.AddMenuItem(MI_CONNECT_FTP,    _T("Connect FTP"));
	res &= popupMenu.AddMenuItem(MI_CONNECT_TELNET, _T("Connect Telnet"));
	res &= popupMenu.AddMenuItem(MI_CONNECT_SSH,    _T("Connect SSH"));
	res &= popupMenu.AddMenuItem(MI_CONNECT_CUSTOM, _T("Connect Custom"));
	res &= popupMenu.AddMenuItem(0,                 _T(""));
	res &= popupMenu.AddMenuItem(MI_CONNECT_EXPORT, _T("Export"));
	res &= popupMenu.AddMenuItem(0,                 _T(""));
	res &= popupMenu.AddMenuItem(MI_CONNECT_HELP,   _T("Help"));
	if(res)
		res = popupMenu.Create();

	// TODO:  Add extra initialization here

	return TRUE;  // return TRUE unless you set the focus to a control
}

//---------------------------------------------------------------------------

void DlgMap::OnOK()
{
	// CRgnDialog::OnOK();
}

void DlgMap::OnCancel()
{
	// CRgnDialog::OnCancel();
}

//---------------------------------------------------------------------------

void DlgMap::ResizeChilds(void)
{
	listMap.MoveWindow(clientRect.left, clientRect.top, clientRect.right - clientRect.left, clientRect.bottom - clientRect.top, TRUE);
}

//---------------------------------------------------------------------------

CString DlgMap::BuildItemName(MMachineInfo* mmi)
{
	CString str;

	if(mmi==NULL){
		str = _T("NULL");
		return str;
	}

	if(!mmi->address.addrIP.IsEmpty()){
		str = mmi->address.addrIP.GetS();
		return str;
	}

	if(!mmi->address.addrIPv6.IsEmpty()){
		str = mmi->address.addrIPv6.GetS();
		return str;
	}

	if(!mmi->address.addrIPX.IsEmpty()){
		str = mmi->address.addrIPX.GetS();
		return str;
	}

	if(!mmi->address.addrMAC.IsEmpty()){
		str = mmi->address.addrMAC.GetS();
		return str;
	}

	str = _T("-");
	return str;
}

//---------------------------------------------------------------------------

bool DlgMap::AddItem(MMachineInfo* mmi)
{
	int itemIdx;
	int res;
	CString str;
	CString sss;

	if(mmi==NULL) return true;

	itemIdx = listMap.GetItemCount();

	str = BuildItemName(mmi);
	res = listMap.InsertItem(itemIdx, str);
	if(res==-1) return false;

	if(!listMap.SetItemData(itemIdx, (DWORD_PTR)mmi))
		return false;

	if(mmi->address.gwMAC){
		str.Empty();
		sss.Empty();
	}
	else{
		str = mmi->address.addrMAC.GetS();
		if(vendorList!=NULL){
			sss = vendorList->GetVendorName(&mmi->address.addrMAC);
			if(!sss.IsEmpty()){
				str += _T(" (");
				str += sss;
				str += _T(")");
			}
		}
	}
	if(!listMap.SetItemText(itemIdx, 1, str))
		return false;

	return true;
}

//---------------------------------------------------------------------------

bool DlgMap::AddGlobalInfo(void)
{
	int res;

	res = listMap.InsertItem(0, _T("Global Info"));
	if(res==-1) return false;
	if(!listMap.SetItemData(0, (DWORD_PTR)&(machInfoList.globalInfo)))
		return false;

	return true;
}

//---------------------------------------------------------------------------

bool DlgMap::UpdateInfo(MMachineInfoList* mil)
{
	MMachineInfo* machInfo;
	int i, cnt;
	bool res;

	listMap.DeleteAllItems();
	machInfoList.Clear();
	if(dlgDet!=NULL)
		dlgDet->UpdateInfo(NULL);

	if(mil==NULL) return true;

	res = true;
	cnt = mil->GetSize();
	for(i=0; i<cnt && res; i++){
		machInfo = mil->GetByIdx(i);
		if(machInfo!=NULL)
			res = machInfoList.Add(machInfo);
	}
	if(!res) return false;

	machInfoList.SortTheListByIp();

	res = true;
	cnt = machInfoList.GetSize();
	for(i=0; i<cnt && res; i++){
		machInfo = machInfoList.GetByIdx(i);
		if(machInfo!=NULL)
			res = AddItem(machInfo);
	}

	return res;
}

//---------------------------------------------------------------------------

bool DlgMap::UpdateInfoEx(MMachineInfoList* mil, bool sortTheListByIP)
{
	int selItemIdx;
	bool globalInfoSelected;
	MMachineInfo selectedMachine;

	MMachineInfo* machInfo;
	MMachineInfo* mach;
	int i, cnt;
	bool res;
	bool newItem;
	CString str, sss;
	bool found;

	if(mil==NULL){
		listMap.DeleteAllItems();
		machInfoList.Clear();
		if(dlgDet!=NULL)
			dlgDet->UpdateInfo(NULL);
		return true;
	}

	selectedMachine.Initialize();

	// remember the previous selection
	globalInfoSelected = false;
	selItemIdx = -1;
	selItemIdx = listMap.GetNextItem(selItemIdx, LVNI_SELECTED);
	if(selItemIdx!=-1){
		str = listMap.GetItemText(selItemIdx, 0);
		if(str.CompareNoCase(_T("Global Info"))==0){
			globalInfoSelected = true;
		}
		else{
			mach = (MMachineInfo*)listMap.GetItemData(selItemIdx);
			selectedMachine.LoadFromPointer(mach);
		}
	}

	// remove previous data
	machInfoList.Clear();

	// update machInfoList
	res = true;
	cnt = mil->GetSize();
	for(i=0; i<cnt && res; i++){
		machInfo = mil->GetByIdx(i);
		if(machInfo!=NULL){
			mach = machInfoList.GetCreateMachineInfoEx(&(machInfo->address), &newItem);
			if(mach==NULL) res = false;
			else{
				// due to removing all previous data, 'newItem' is ALLWAYS true !
				if(newItem) mach->LoadFromPointer(machInfo);
				else        mach->LoadFromPointerUpdate(machInfo);
			}
		}
	}
	if(!res) return false;

	machInfoList.globalInfo.LoadFromPointer(&(mil->globalInfo));

	// sort machInfoList
	if(sortTheListByIP)
		machInfoList.SortTheListByIp();

	// redraw the whole list
	listMap.SetRedraw(FALSE);
	listMap.DeleteAllItems();

	if(!machInfoList.globalInfo.IsEmpty()) res = AddGlobalInfo();
	else                                   res = true;

	cnt = machInfoList.GetSize();
	for(i=0; i<cnt && res; i++){
		machInfo = machInfoList.GetByIdx(i);
		if(machInfo!=NULL)
			res = AddItem(machInfo);
	}

	if(dlgDet!=NULL){
		if(globalInfoSelected){
			dlgDet->UpdateGlobalInfo(&(machInfoList.globalInfo));
		}
		else{
			found = false;
			cnt = listMap.GetItemCount();
			for(i=0; i<cnt && !found; i++){
				str = listMap.GetItemText(i, 0);
				if(str.CompareNoCase(_T("Global Info"))!=0){
					mach = (MMachineInfo*)listMap.GetItemData(i);
					if(mach->address.addrIP.CompareWith(&selectedMachine.address.addrIP)==0){
						// found
						listMap.SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
						dlgDet->UpdateInfo(mach);
						found = true;
					}
				}
			}
		}
	}

	listMap.SetRedraw(TRUE);

	if(globalInfoSelected)
		listMap.SetItemState(0, LVIS_SELECTED, LVIS_SELECTED);

	return res;
}

//---------------------------------------------------------------------------

void DlgMap::OnLvnItemchangedMapList(NMHDR *pNMHDR, LRESULT *pResult)
{
	MMachineInfo* machInfo;
	CString str;

	LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);

	if(pNMLV!=NULL){
		if(pNMLV->uNewState==(LVIS_FOCUSED | LVIS_SELECTED)){
			str = listMap.GetItemText(pNMLV->iItem, 0);
			if(str.CompareNoCase(_T("Global Info"))==0)
			{
				if(dlgDet!=NULL)
					dlgDet->UpdateGlobalInfo(&(machInfoList.globalInfo));
			}
			else{
				machInfo = (MMachineInfo*)listMap.GetItemData(pNMLV->iItem);
				if(dlgDet!=NULL)
					dlgDet->UpdateInfo(machInfo);
			}
		}
	}
	*pResult = 0;
}

void DlgMap::OnNMRclickMapList(NMHDR *pNMHDR, LRESULT *pResult)
{
	DWORD pos;
	POINTS pp;
	CString str;

	pos = GetMessagePos();

	LPNMITEMACTIVATE pNMIA = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
	if(pNMIA!=NULL){
		str = listMap.GetItemText(pNMIA->iItem, 0);
		if(str.CompareNoCase(_T("Global Info"))==0){
			if(SetPopupMenu(-1)){
				pp = MAKEPOINTS(pos);
				popupMenu.Show(this, pp.x, pp.y);
			}
		}
		else{
			if(SetPopupMenu(pNMIA->iItem)){
				pp = MAKEPOINTS(pos);
				popupMenu.Show(this, pp.x, pp.y);
			}
		}
	}
	*pResult = 0;
}

BOOL DlgMap::OnCommand(WPARAM wParam, LPARAM lParam)
{
	if(lParam!=0)
		return CRgnDialog::OnCommand(wParam, lParam);

	WORD val;
	val = HIWORD(wParam);
	if(val!=0){
		// not from a menu
		return CRgnDialog::OnCommand(wParam, lParam);
	}

	val = LOWORD(wParam);
	ProcessMenuItemCommand(val);

	return TRUE;
}

//---------------------------------------------------------------------------

bool DlgMap::SetPopupMenu(int selItemIdx)
{
	MMachineInfo* machInfo;

	if(selItemIdx<0) machInfo = NULL;
	else             machInfo = (MMachineInfo*)listMap.GetItemData(selItemIdx);

	if(machInfo!=NULL){
		if(machInfo->IsTcpPortOpen(80)) popupMenu.EnableMenuItem(MI_CONNECT_HTTP);
		else                            popupMenu.DisableMenuItem(MI_CONNECT_HTTP);

		if(machInfo->IsTcpPortOpen(443)) popupMenu.EnableMenuItem(MI_CONNECT_HTTPS);
		else                             popupMenu.DisableMenuItem(MI_CONNECT_HTTPS);

		if(machInfo->IsTcpPortOpen(21)) popupMenu.EnableMenuItem(MI_CONNECT_FTP);
		else                            popupMenu.DisableMenuItem(MI_CONNECT_FTP);

		if(machInfo->IsTcpPortOpen(23)) popupMenu.EnableMenuItem(MI_CONNECT_TELNET);
		else                            popupMenu.DisableMenuItem(MI_CONNECT_TELNET);

		if(machInfo->IsTcpPortOpen(22)) popupMenu.EnableMenuItem(MI_CONNECT_SSH);
		else                            popupMenu.DisableMenuItem(MI_CONNECT_SSH);

		// TODO implement MI_CONNECT_CUSTOM
		//popupMenu.EnableMenuItem(MI_CONNECT_CUSTOM);
		popupMenu.DisableMenuItem(MI_CONNECT_CUSTOM);
	}
	else{
		popupMenu.DisableMenuItem(MI_CONNECT_HTTP);
		popupMenu.DisableMenuItem(MI_CONNECT_HTTPS);
		popupMenu.DisableMenuItem(MI_CONNECT_FTP);
		popupMenu.DisableMenuItem(MI_CONNECT_TELNET);
		popupMenu.DisableMenuItem(MI_CONNECT_SSH);
		popupMenu.DisableMenuItem(MI_CONNECT_CUSTOM);
	}

	return true;
}

void DlgMap::ProcessMenuItemCommand(WORD menuItemIdIn)
{

	MMachineInfo* machInfo;
	int itemIdx;

	listMap.RedrawWindow();

	// TODO FUCKING COMPLETE THIS FUNCTION !!!

	machInfo = NULL;
	itemIdx = -1;
	itemIdx = listMap.GetNextItem(itemIdx, LVNI_SELECTED);
	if(itemIdx!=-1)
		machInfo = (MMachineInfo*)listMap.GetItemData(itemIdx);

	switch(menuItemIdIn){
		case MI_CONNECT_HTTP :
			if(machInfo!=NULL)
				LaunchHttp(machInfo->address.addrIP.GetS());
			break;

		case MI_CONNECT_HTTPS :
			if(machInfo!=NULL)
				LaunchHttps(machInfo->address.addrIP.GetS());
			break;

		case MI_CONNECT_FTP :
			if(machInfo!=NULL)
				LaunchFtp(machInfo->address.addrIP.GetS());
			break;

		case MI_CONNECT_TELNET :
			if(machInfo!=NULL)
				LaunchTelnet(machInfo->address.addrIP.GetS());
			break;

		case MI_CONNECT_SSH :
			if(machInfo!=NULL)
				LaunchSsh(machInfo->address.addrIP.GetS());
			break;

		case MI_CONNECT_CUSTOM :
			break;

		case MI_CONNECT_EXPORT :
			break;

		case MI_CONNECT_HELP :
			ShowHelp();
			break;

		default:
			break;
	}
}

//---------------------------------------------------------------------------

bool DlgMap::ExportAsXml(CString fileNameIn, CString startTimeIn, WorkCfg *cfgIn, LastScanType lstIn)
{
	MMachineInfo* machInfo;
	ExportInfo exportInfo;
	int i, cnt;
	bool res;

	exportInfo.destFileName = fileNameIn;
	exportInfo.timeStamp = startTimeIn;
	exportInfo.application = _T("Calin's Humble Scanner");
	exportInfo.version = _T(APPLICATION_VERSION);
	exportInfo.workCfg.LoadFromPointer(cfgIn);

	exportInfo.scanType = lstIn;

	res = exportXML.BeginExport(&exportInfo);
	if(!res){
		exportXML.EndExport();
		return false;
	}

	if(!machInfoList.globalInfo.IsEmpty())
		res = exportXML.ExportGlobalInfo(&(machInfoList.globalInfo));

	cnt = machInfoList.GetSize();
	for(i=0; i<cnt && res; i++){
		machInfo = machInfoList.GetByIdx(i);
		if(machInfo!=NULL)
			res = exportXML.ExportMachine(machInfo, vendorList, mcast4List, portDescList);
	}
	if(!res){
		exportXML.EndExport();
		return false;
	}

	return exportXML.EndExport();
}

//---------------------------------------------------------------------------

int CALLBACK CompareByIP(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
	CListCtrl    *listCtrl;
	MMachineInfo *mmi1;
	MMachineInfo *mmi2;
	MAddressIP   *maip1;
	MAddressIP   *maip2;
	MAddressMAC  *mac1;
	MAddressMAC  *mac2;
	int retVal;

	listCtrl = (CListCtrl*)lParamSort;
	if(listCtrl==NULL) return 0;

	mmi1 = (MMachineInfo *)lParam1;
	mmi2 = (MMachineInfo *)lParam2;

	if(mmi1==NULL) return -1;
	if(mmi2==NULL) return 1;

	maip1 = &mmi1->address.addrIP;
	maip2 = &mmi2->address.addrIP;
	retVal = maip1->CompareWith(maip2);
	if(retVal==0){
		// should not happend but, just in case ...
		mac1 = &mmi1->address.addrMAC;
		mac2 = &mmi2->address.addrMAC;
		retVal = mac1->Compare(mac2);
	}

	return retVal;
}

int CALLBACK CompareByMAC(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
	CListCtrl    *listCtrl;
	MMachineInfo *mmi1;
	MMachineInfo *mmi2;
	MAddressIP   *maip1;
	MAddressIP   *maip2;
	MAddressMAC  *mac1;
	MAddressMAC  *mac2;
	int retVal;

	listCtrl = (CListCtrl*)lParamSort;
	if(listCtrl==NULL) return 0;

	mmi1 = (MMachineInfo *)lParam1;
	mmi2 = (MMachineInfo *)lParam2;

	if(mmi1==NULL) return -1;
	if(mmi2==NULL) return 1;

	mac1 = &mmi1->address.addrMAC;
	mac2 = &mmi2->address.addrMAC;
	retVal = mac1->Compare(mac2);
	if(retVal==0){
		// should not happend but, just in case ...
		maip1 = &mmi1->address.addrIP;
		maip2 = &mmi2->address.addrIP;
		retVal = maip1->CompareWith(maip2);
	}

	return retVal;
}

void DlgMap::OnLvnColumnclickMapList(NMHDR *pNMHDR, LRESULT *pResult)
{
	int colNo;

	LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);

	*pResult = 0;
	if(pNMLV==NULL) return;

	if(pNMLV->iItem!=0xFFFFFFFF)
		return;

	colNo = pNMLV->iSubItem;
	switch(colNo){
		case 0 :
			// sort by IP Address
			listMap.SortItems(CompareByIP, (LPARAM)&listMap);
			break;

		case 1 :
			// sort by MAC Address
			listMap.SortItems(CompareByMAC, (LPARAM)&listMap);
			break;

		default:
			// do nothing
			break;
	}
}

//---------------------------------------------------------------------------

void DlgMap::LaunchHttp(CString strIn)
{
	CString str;

	str  = _T("http://");
	str += strIn;
	ShellExecute(NULL, NULL, str, NULL, NULL, SW_SHOW);
}

void DlgMap::LaunchHttps(CString strIn)
{
	CString str;

	str  = _T("https://");
	str += strIn;
	ShellExecute(NULL, NULL, str, NULL, NULL, SW_SHOW);
}

void DlgMap::LaunchFtp(CString strIn)
{
	CString str;

	str  = _T("ftp://");
	str += strIn;
	ShellExecute(NULL, NULL, str, NULL, NULL, SW_SHOW);
}

void DlgMap::LaunchTelnet(CString strIn)
{
	ShellExecute(NULL, NULL, _T("telnet.exe"), strIn, NULL, SW_SHOW);
}

void DlgMap::LaunchSsh(CString strIn)
{
	ShellExecute(NULL, NULL, _T("putty.exe"), strIn, NULL, SW_SHOW);
}

//---------------------------------------------------------------------------
